﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using NUnit.Framework;
using ServiceStack.Common.Tests.Models;
using ServiceStack.DataAnnotations;
using ServiceStack.Text;

namespace ServiceStack.Common.Tests;

public class ExpressionUtilsTests
{
    abstract class AbstractBase
    {
        public string BaseMember { get; set; }
    }

    class Derived : AbstractBase
    {
        public string DerivedMember { get; set; }
    }

    [Test]
    public void Does_GetMemberName()
    {
        Assert.That(ExpressionUtils.GetMemberName((Poco x) => x.Name),
            Is.EqualTo("Name"));

        Assert.That(ExpressionUtils.GetMemberName((ModelWithFieldsOfNullableTypes x) => x.NId),
            Is.EqualTo("NId"));
    }

    public Expression<Func<T>> GetAssignmentExpression<T>(Expression<Func<T>> expr)
    {
        return expr;
    }

    [Test]
    public void Can_get_assigned_constants()
    {
        Assert.That(GetAssignmentExpression(() => new Poco { Name = "Foo" }).AssignedValues(),
            Is.EquivalentTo(new Dictionary<string, object> {
                {"Name", "Foo"}
            }));
    }

    [Test]
    public void Can_get_assigned_expressions()
    {
        2.Times(i =>
        {
            Assert.That(GetAssignmentExpression(() => new Poco { Name = i % 2 == 0 ? "Foo" : "Bar" }).AssignedValues(),
                Is.EquivalentTo(new Dictionary<string, object> {
                    { "Name", i % 2 == 0 ? "Foo" : "Bar" }
                }));
        });
    }

    [Test]
    public void Can_get_fields_list_from_property_expression()
    {
        Assert.That(ExpressionUtils.GetFieldNames((Poco x) => x.Name),
            Is.EquivalentTo(new[] { "Name" }));

        Assert.That(ExpressionUtils.GetFieldNames((Poco x) => x.Id),
            Is.EquivalentTo(new[] { "Id" }));
    }

    [Test]
    public void Can_get_fields_list_from_anon_object()
    {
        Assert.That(ExpressionUtils.GetFieldNames((Poco x) => new { x.Id, x.Name }),
            Is.EquivalentTo(new[] { "Id", "Name" }));
    }

    [Test]
    public void Can_get_fields_list_from_Typed_object()
    {
        Assert.That(ExpressionUtils.GetFieldNames((Poco x) => new Poco { Id = x.Id, Name = x.Name }),
            Is.EquivalentTo(new[] { "Id", "Name" }));
    }

    [Test]
    public void Can_get_fields_list_from_array()
    {
        Assert.That(ExpressionUtils.GetFieldNames((Poco x) => new[] { "Id", "Name" }),
            Is.EquivalentTo(new[] { "Id", "Name" }));

        var id = "Id";

        Assert.That(ExpressionUtils.GetFieldNames((Poco x) => new[] { id, "Na" + "me" }),
            Is.EquivalentTo(new[] { "Id", "Name" }));
    }

    [Test]
    public void Can_get_fields_list_from_list()
    {
        var list = new List<string> { "Id", "Name" };

        Assert.That(ExpressionUtils.GetFieldNames((Poco x) => list),
            Is.EquivalentTo(new[] { "Id", "Name" }));
    }

    [Test]
    public void Can_get_fields_from_abstract_base_class()
    {
        Assert.That(ExpressionUtils.GetFieldNames<Derived>(p => p.BaseMember),
            Is.EquivalentTo(new[] {"BaseMember"}));
        Assert.That(ExpressionUtils.GetFieldNames<Derived>(p => p.DerivedMember),
            Is.EquivalentTo(new[] { "DerivedMember" }));
    }

    public class Question
    {
        public int Id { get; set; }
        public string Text { get; set; }

        [CustomField("json")]
        public List<Answer> Answers { get; set; }
    }

    public class Answer
    {
        public int Id { get; set; }
        public string Text { get; set; }
    }

    [Test]
    public void Can_get_assigned_ComplexTypes()
    {
        var assignedValues = GetAssignmentExpression(() => new Question
        {
            Id = 1,
            Answers = new List<Answer>
            {
                new Answer { Id = 1, Text = "Q1 Answer1" }
            }
        }).AssignedValues();

        Assert.That(assignedValues.Count, Is.EqualTo(2));
        var assignedValue = (List<Answer>)assignedValues["Answers"];
        Assert.That(assignedValue[0].Text, Is.EqualTo("Q1 Answer1"));
    }
}